{
 "cells": [
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "'3.2.0'"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "import torch\n",
    "import triton\n",
    "import triton.language as tl\n",
    "import os\n",
    "from copy import deepcopy\n",
    "import math\n",
    "os.environ['TRITON_PRINT_AUTOTUNING'] = '1'\n",
    "triton.__version__"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# triton code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": [
    "# @triton.autotune([triton.Config({'BLOCK_SIZE_N': bsn, 'CHUNK_E':ce}, num_stages=ns, num_warps=nw)\n",
    "#                  for bsn in [32, 64, 128]\n",
    "#                  for ce in [32, 64, 128]\n",
    "#                  for ns in [1,2,4]\n",
    "#                  for nw in [4,8]\n",
    "#                  ], key=['B', 'H', 'FP32'])\n",
    "@triton.jit\n",
    "def _lighting_attention_encode_kernel(QKV, KV, Y, SLOPE_RATE, NUM_PADDDINGS,\n",
    "                                    qkv_sb, qkv_sn, qkv_sh, qkv_sd,\n",
    "                                    kv_sb, kv_sh, kv_sd, kv_se,\n",
    "                                    y_sb, y_sh, y_sn, y_sd,\n",
    "                                    B, N, H, D:tl.constexpr, FP32:tl.constexpr,\n",
    "                                    CHUNK_E:tl.constexpr,BLOCK_SIZE_N: tl.constexpr,\n",
    "                                    ):\n",
    "    off_b = tl.program_id(0).to(tl.int64)\n",
    "    off_h = tl.program_id(1).to(tl.int64)\n",
    "    off_e = (tl.program_id(2) * CHUNK_E).to(tl.int64)\n",
    "\n",
    "    QKV = QKV + off_b * qkv_sb + off_h * qkv_sh\n",
    "    KV = KV + off_b * kv_sb + off_h * kv_sh\n",
    "    Y = Y + off_b * y_sb + off_h * y_sh\n",
    "    SLOPE_RATE += off_h\n",
    "    NUM_PADDDINGS += off_b\n",
    "    dd = tl.arange(0, D)\n",
    "    nn = tl.arange(0, BLOCK_SIZE_N)\n",
    "    ee = tl.arange(0, CHUNK_E)\n",
    "\n",
    "    num_paddings = tl.load(NUM_PADDDINGS)\n",
    "    q_ptrs = QKV + (nn[:, None] + num_paddings) * qkv_sn + dd[None, :]\n",
    "    k_ptrs = QKV + (nn[None, :] + num_paddings) * qkv_sn + dd[:, None] + D\n",
    "    v_ptrs = QKV + (nn[:, None] + num_paddings) * qkv_sn + ee[None, :] + off_e + 2*D\n",
    "    kv_ptrs = KV + dd[:, None] * kv_sd + ee[None, :] + off_e\n",
    "    y_pts = Y + (nn[:, None] + num_paddings) * y_sn + ee[None, :] + off_e\n",
    "\n",
    "    slope_rate = tl.load(SLOPE_RATE).to(tl.float32)\n",
    "\n",
    "    array = (nn + 1)\n",
    "    q_decay = tl.exp(-1. * slope_rate * array)[:, None]\n",
    "    k_decay = tl.exp(-1. * slope_rate * (BLOCK_SIZE_N - array))[None, :]\n",
    "    index = array[:, None] - array[None, :]\n",
    "    s_index = slope_rate * index\n",
    "    s_index = tl.where(index >= 0, -s_index, float('-inf'))\n",
    "    diag_decay = tl.exp(s_index)\n",
    "\n",
    "    if FP32:\n",
    "        dtype = tl.float32\n",
    "    else:\n",
    "        dtype = tl.bfloat16\n",
    "    kv = tl.zeros((D, CHUNK_E), dtype=tl.float32)\n",
    "    \n",
    "    for start_n in tl.range(num_paddings, N, BLOCK_SIZE_N):\n",
    "        mask_nn = (nn + start_n) < N  \n",
    "        m = tl.minimum(N-start_n, BLOCK_SIZE_N)\n",
    "        if m < BLOCK_SIZE_N:\n",
    "            tmp = -1. * slope_rate * (m - array)\n",
    "            tmp = tl.where((m-array)<0, 0., tmp)\n",
    "            k_decay = tl.exp(tmp)[None, :]\n",
    "            \n",
    "        q = tl.load(q_ptrs, mask=mask_nn[:, None], other=0.).to(dtype)\n",
    "        k = tl.load(k_ptrs, mask=mask_nn[None, :], other=0.).to(dtype)\n",
    "        v = tl.load(v_ptrs, mask=mask_nn[:, None], other=0.).to(dtype)\n",
    "\n",
    "        qkv_none_diag = tl.dot((q * q_decay).to(dtype), kv.to(dtype))\n",
    "\n",
    "        qk = tl.dot(q, k) * diag_decay\n",
    "        qkv_diag = tl.dot(qk.to(dtype), v)\n",
    "        y = qkv_diag + qkv_none_diag\n",
    "        block_decay = tl.exp(-1. * slope_rate * m)\n",
    "        kv = kv * block_decay + tl.dot((k * k_decay).to(dtype), v)\n",
    "        # kv = tl.dot(tl.permute(k, (1,0)), k)\n",
    "        \n",
    "        tl.store(y_pts, y, mask=mask_nn[:, None])\n",
    "\n",
    "        q_ptrs += BLOCK_SIZE_N * qkv_sn\n",
    "        k_ptrs += BLOCK_SIZE_N * qkv_sn\n",
    "        v_ptrs += BLOCK_SIZE_N * qkv_sn\n",
    "        y_pts += BLOCK_SIZE_N * y_sn\n",
    "        \n",
    "    tl.store(kv_ptrs, kv)\n",
    "\n",
    "# @triton.autotune([triton.Config({'CHUNK_E':ce}, num_stages=ns, num_warps=nw)\n",
    "#                  for ce in [32, 64, 128]\n",
    "#                  for ns in [1,2,3,4]\n",
    "#                  for nw in [2,4,8]\n",
    "#                  ], key=['B', 'N', 'H', 'FP32'])\n",
    "@triton.jit\n",
    "def _lighting_attention_decode_kernel(QKV, KV, Y, SLOPE_RATE,\n",
    "                                    qkv_sb, qkv_sn, qkv_sh, qkv_sd,\n",
    "                                    kv_sb, kv_sh, kv_sd, kv_se,\n",
    "                                    y_sb, y_sh, y_sn, y_sd,\n",
    "                                    B, N, H, D:tl.constexpr, FP32:tl.constexpr,\n",
    "                                    CHUNK_E:tl.constexpr\n",
    "                                    ):\n",
    "    off_b = tl.program_id(0)\n",
    "    off_h = tl.program_id(1)\n",
    "    off_e = tl.program_id(2) * CHUNK_E \n",
    "\n",
    "    QKV += off_b * qkv_sb + off_h * qkv_sh\n",
    "    KV += off_b * kv_sb + off_h * kv_sh\n",
    "    Y += off_b * y_sb + off_h * y_sh\n",
    "    SLOPE_RATE += off_h\n",
    "    dd = tl.arange(0, D)\n",
    "    ee = tl.arange(0, CHUNK_E)\n",
    "\n",
    "    q_ptrs = QKV + dd\n",
    "    v_prts = QKV + 2*D + ee + off_e\n",
    "    kv_ptrs = KV + dd[:, None] * kv_sd + ee[None, :] + off_e\n",
    "    y_ptrs = Y + ee + off_e\n",
    "\n",
    "    slope_rate = tl.load(SLOPE_RATE).to(tl.float32)\n",
    "    ratio = tl.exp(-1. * slope_rate)\n",
    "\n",
    "    if FP32:\n",
    "        dtype = tl.float32\n",
    "    else:\n",
    "        dtype = tl.bfloat16\n",
    "    kv = tl.load(kv_ptrs).to(dtype)\n",
    "    q = tl.load(q_ptrs).to(dtype)\n",
    "    k = tl.load(q_ptrs+D).to(dtype)\n",
    "    v = tl.load(v_prts).to(dtype)\n",
    "\n",
    "    kv = ratio * kv + k[:, None] * v[None, :]\n",
    "    y = tl.sum(q[:, None] * kv, axis=0)\n",
    "    tl.store(kv_ptrs, kv)\n",
    "    tl.store(y_ptrs, y)\n",
    "\n",
    "\n",
    "def lighting_attention_encode(qkv, slope_rate, attention_mask=None, fp32=False):\n",
    "    # b, n, h, d\n",
    "    b, n, h, d3 = qkv.shape\n",
    "    d = d3 // 3\n",
    "    assert math.log2(d).is_integer(), 'd must be power of 2'\n",
    "    slope_rate = slope_rate.squeeze()\n",
    "    kv = torch.empty(b, h, d, d).to(torch.float32).to(qkv.device)\n",
    "    y = torch.zeros(b, h, n, d, device=qkv.device, dtype=qkv.dtype)\n",
    "    if attention_mask is not None:\n",
    "        assert attention_mask[-1, :].min().values != 0, 'please use left_padding'\n",
    "        num_paddings = n - attention_mask.sum(-1)\n",
    "    else:\n",
    "        num_paddings = torch.full((b,), 0, device=qkv.device, dtype=torch.int32)\n",
    "    \n",
    "    # A800的配置\n",
    "    kwargs = {'BLOCK_SIZE_N':32, 'CHUNK_E': 64, 'num_warps': 4, 'num_stages':2 if fp32 else 1}\n",
    "    # 第三个维度是我后来看lighting attention github上的代码学习到的！\n",
    "    grids = lambda meta: (b, h, triton.cdiv(d, meta['CHUNK_E']))\n",
    "    _lighting_attention_encode_kernel[grids](qkv, kv, y, slope_rate, num_paddings,\n",
    "                                            *qkv.stride(),\n",
    "                                            *kv.stride(),\n",
    "                                            *y.stride(),\n",
    "                                            b, n, h, d, fp32,\n",
    "                                            **kwargs,\n",
    "                                            )\n",
    "    return y, kv\n",
    "\n",
    "def lighting_attention_decode(qkv, slope_rate, kv, fp32=False):\n",
    "    # b, n, h, d\n",
    "    b, n, h, d3 = qkv.shape\n",
    "    assert n == 1, 'decoing phase need n=1'\n",
    "    d = d3 // 3\n",
    "    slope_rate = slope_rate.squeeze()\n",
    "    y = torch.empty(b, h, n, d, device=qkv.device, dtype=qkv.dtype)\n",
    "    \n",
    "    # A800的配置\n",
    "    kwargs = {'CHUNK_E': 32, 'num_warps': 8, 'num_stages':4 if fp32 else 2}\n",
    "    grids = lambda meta: (b, h, triton.cdiv(d, meta['CHUNK_E']))\n",
    "    _lighting_attention_decode_kernel[grids](qkv, kv, y, slope_rate,\n",
    "                                            *qkv.stride(),\n",
    "                                            *kv.stride(),\n",
    "                                            *y.stride(),\n",
    "                                            b, n, h, d, fp32,\n",
    "                                            **kwargs\n",
    "                                            )\n",
    "    return y, kv\n",
    "\n",
    "def triton_lighting_attention(qkv, slope_rate, past_key_value=None, attention_mask=None, fp32=False):\n",
    "    if past_key_value is None:\n",
    "        y, kv = lighting_attention_encode(qkv, slope_rate, attention_mask, fp32)\n",
    "    else:\n",
    "        y, kv = lighting_attention_decode(qkv, slope_rate, past_key_value, fp32)\n",
    "    return y, kv\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# torch code"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [],
   "source": [
    "BLOCK = 256\n",
    "def torch_lighting_attention(qkv, slope_rate, past_key_value=None, attention_mask=None):\n",
    "    n = qkv.size(1)\n",
    "    q, k, v = torch.split(qkv, [qkv.size(-1)//3] * 3, dim=3)\n",
    "    # [b, h, l, d]\n",
    "    q = q.transpose(1, 2)\n",
    "    k = k.transpose(1, 2)\n",
    "    v = v.transpose(1, 2)\n",
    "\n",
    "    if past_key_value is None:\n",
    "        offset = q.shape[-2]\n",
    "    else:\n",
    "        offset = 1\n",
    "\n",
    "    # for align with metaseq\n",
    "    ratio = torch.exp(-slope_rate)\n",
    "\n",
    "    # only use for the first time\n",
    "    if past_key_value is None:\n",
    "        slope_rate = slope_rate.to(torch.float32)\n",
    "        if attention_mask is not None:\n",
    "            v = v.masked_fill((1 - attention_mask).unsqueeze(1).unsqueeze(-1).to(torch.bool), 0)\n",
    "        # print(v[0, 0, :32])\n",
    "        NUM_BLOCK = (n + BLOCK - 1) // BLOCK\n",
    "        b, h, n, d = q.shape\n",
    "        e = v.shape[-1]\n",
    "        # other\n",
    "        array = torch.arange(BLOCK).to(q) + 1\n",
    "        q_decay = torch.exp(-slope_rate * array.reshape(-1, 1)) # h, bn, 1 \n",
    "        k_decay = torch.exp(-slope_rate * (BLOCK - array.reshape(-1, 1)))\n",
    "        index = array[:, None] - array[None, :]\n",
    "        s_index = slope_rate * index[\n",
    "            None,\n",
    "            None,\n",
    "        ]\n",
    "        s_index = torch.where(index >= 0, -s_index, float(\"-inf\"))\n",
    "        diag_decay = torch.exp(s_index)\n",
    "\n",
    "        kv = torch.zeros(b, h, d, e).to(torch.float32).to(q.device)\n",
    "        output = torch.empty((b, h, n, e), dtype=q.dtype, device=q.device)\n",
    "        for i in range(NUM_BLOCK):\n",
    "            si = i * BLOCK\n",
    "            ei = min(si + BLOCK, n)\n",
    "            m = ei - si\n",
    "            qi = q[:, :, si:ei].contiguous()\n",
    "            ki = k[:, :, si:ei].contiguous()\n",
    "            vi = v[:, :, si:ei].contiguous()\n",
    "            qkv_none_diag = torch.matmul(qi * q_decay[:, :m], kv).to(torch.float32)\n",
    "\n",
    "            # diag\n",
    "            qk = torch.matmul(qi, ki.transpose(-1, -2)).to(torch.float32) * diag_decay[:, :, :m, :m]\n",
    "            qkv_diag = torch.matmul(qk, vi.to(torch.float32))\n",
    "            block_decay = torch.exp(-slope_rate * m)\n",
    "            output[:, :, si:ei] = qkv_none_diag + qkv_diag\n",
    "            kv = block_decay * kv + torch.matmul((ki * k_decay[:, -m:]).transpose(-1, -2).to(vi.dtype), vi)\n",
    "    else:\n",
    "        kv = past_key_value\n",
    "        output = []\n",
    "        for i in range(n):\n",
    "            kv = ratio * kv + torch.einsum(\n",
    "                \"... n d, ... n e -> ... d e\",\n",
    "                k[:, :, i:i + 1],\n",
    "                v[:, :, i:i + 1],\n",
    "            )\n",
    "            qkv = torch.einsum(\"... n e, ... e d -> ... n d\", q[:, :, i:i + 1], kv.to(q.dtype))\n",
    "            output.append(qkv)\n",
    "        output = torch.concat(output, dim=-2)\n",
    "    return output, kv\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 精度测试\n",
    "- 这个attention真的离谱，精度差很多\n",
    "- 原始代码中既有fp32也有bf16的矩阵乘"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "dtype = torch.bfloat16\n",
    "device = 'cuda'\n",
    "b, n, h, d = 8, 1020, 64, 128\n",
    "qkv = torch.randn(b, n, h, d*3, device=device, dtype=dtype)\n",
    "slope_rate = torch.rand(h, 1, 1, device=device, dtype=torch.float32) + 1e-3\n",
    "gold_qkv = torch.rand_like(qkv, dtype=torch.float32).copy_(qkv)\n",
    "attn_mask = torch.ones(b, n, device=device, dtype=torch.int32)\n",
    "attn_mask[:, :30] = 0 "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(2.0440, device='cuda:0') tensor(0.0366, device='cuda:0')\n",
      "tensor(2.4799, device='cuda:0') tensor(0.0411, device='cuda:0')\n",
      "tensor(0.1576, device='cuda:0') tensor(0.0031, device='cuda:0')\n",
      "tensor(0.0861, device='cuda:0') tensor(0.0020, device='cuda:0')\n"
     ]
    }
   ],
   "source": [
    "x1, x2 = torch_lighting_attention(qkv, slope_rate, attention_mask=attn_mask)\n",
    "y1, y2 = triton_lighting_attention(qkv, slope_rate, attention_mask=attn_mask, fp32=False)\n",
    "z1, z2 = torch_lighting_attention(gold_qkv, slope_rate, attention_mask=attn_mask)\n",
    "print((x1-z1).abs().max(), (x1-z1).abs().mean())\n",
    "print((y1-z1).abs().max(), (y1-z1).abs().mean())\n",
    "print((x2-z2).abs().max(), (x2-z2).abs().mean())\n",
    "print((y2-z2).abs().max(), (y2-z2).abs().mean())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/plain": [
       "tensor(nan, device='cuda:0', dtype=torch.bfloat16)"
      ]
     },
     "execution_count": 8,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "y1.min()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [],
   "source": [
    "dtype = torch.bfloat16\n",
    "device = 'cuda'\n",
    "b, n, h, d = 8, 1, 64, 128\n",
    "qkv = torch.randn(b, n, h, d*3, device=device, dtype=dtype)\n",
    "kv =  torch.randn(b, h, d, d, dtype=torch.float32, device=device)\n",
    "slope_rate = torch.rand(h, 1, 1, device=device, dtype=dtype)\n",
    "gold_qkv = torch.rand_like(qkv, dtype=torch.float32).copy_(qkv)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "tensor(0.3089, device='cuda:0') tensor(0.0257, device='cuda:0')\n",
      "tensor(0.2961, device='cuda:0') tensor(0.0239, device='cuda:0')\n",
      "tensor(0.0312, device='cuda:0') tensor(0.0009, device='cuda:0')\n",
      "tensor(0.0358, device='cuda:0') tensor(0.0015, device='cuda:0')\n"
     ]
    }
   ],
   "source": [
    "x1, x2 = torch_lighting_attention(qkv, slope_rate, deepcopy(kv))\n",
    "y1, y2 = lighting_attention_decode(qkv, slope_rate, deepcopy(kv), fp32=False)\n",
    "z1, z2 = torch_lighting_attention(gold_qkv, slope_rate, deepcopy(kv))\n",
    "print((x1-z1).abs().max(), (x1-z1).abs().mean())\n",
    "print((y1-z1).abs().max(), (y1-z1).abs().mean())\n",
    "print((x2-z2).abs().max(), (x2-z2).abs().mean())\n",
    "print((y2-z2).abs().max(), (y2-z2).abs().mean())"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 性能测试\n",
    "- encode 阶段bf16比fp32快一倍"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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Z2dTUFBAYGBgfQypSpWNFeCmm26iffv2xMfHs3HjRmbOnElaWhoffvhhteMo31Zdn4Yc66ZNm0hMTKSwsJCQkBA++ugjevToQWpqapP7PKsaKzSdz7PcO++8w/fff8+aNWsqbGtqf1cVmpqJESNGuB737t2bQYMG0b59e957770G/2KQ+nPjjTe6Hvfq1YvevXvTuXNnkpOTGTp0qI2V1d7UqVPZvHkz33zzjd2l1LuqxnrHHXe4Hvfq1Yu4uDiGDh3K7t276dy5c0OXWWsJCQmkpqaSlZXFv//9byZMmMCKFSvsLqteVDXWHj16NJnPE2D//v3cc889LF26lICAALvLqXc6PNdMRURE0LVrV3bt2kVsbCzFxcWcOHHCrc+hQ4eIjY0FIDY2tsLVDuXt8j6eqLy2ymo/dWyZmZlu20tLSzl27FijH3+nTp1o2bIlu3btAhrfWKdNm8aiRYtYvnw5bdq0ca2vq9/ZqvqEhYU1+H8mqhprZQYNGgTg9rk2hrH6+fnRpUsXBgwYwLx58+jTpw/PPPNMk/w8qxprZRrr5wnW4bfMzEz69++Pj48PPj4+rFixgn/84x/4+PgQExPTpD5bhaZmKjc3l927dxMXF8eAAQPw9fVl2bJlru1paWmkp6e7jsEnJiayadMmty/cpUuXEhYW5trl7Ik6duxIbGys29iys7NZvXq129hOnDjBunXrXH2++uornE6n6x+zxMREvv76a0pKSlx9li5dSkJCgsccmqvMjz/+yNGjR4mLiwMaz1iNMUybNo2PPvqIr776qsLhwrr6nU1MTHR7jfI+p557Ut/ONNbKpKamArh9ro1hrKdzOp0UFRU1qc+zKuVjrUxj/jyHDh3Kpk2bSE1NdS0DBw5k3LhxrsdN6rNt0NPOxTa///3vTXJystm7d69ZuXKlGTZsmGnZsqXJzMw0xliXhLZr18589dVXZu3atSYxMdEkJia6nl9+SeiVV15pUlNTzZIlS0yrVq084pYDOTk5Zv369Wb9+vUGME8//bRZv369+eGHH4wx1i0HIiIizCeffGI2btxorrnmmkpvOdCvXz+zevVq880335jzzjvP7TL8EydOmJiYGDN+/HizefNm884775igoKAGv+VAdWPNyckx999/v0lJSTF79+41X375penfv78577zzTGFhYaMa65133mnCw8NNcnKy22XZ+fn5rj518TtbfhnzjBkzzLZt28xzzz3X4Jcxn2msu3btMo888ohZu3at2bt3r/nkk09Mp06dzKWXXtqoxjpr1iyzYsUKs3fvXrNx40Yza9Ys43A4zBdffGGMaTqf55nG2lQ+z+qcfnVgU/psFZqaiRtuuMHExcUZPz8/07p1a3PDDTeYXbt2ubYXFBSY3/3udyYyMtIEBQWZX//61+bgwYNur7Fv3z4zYsQIExgYaFq2bGl+//vfm5KSkoYeSgXLly83QIVlwoQJxhjrtgN/+tOfTExMjPH39zdDhw41aWlpbq9x9OhRM3bsWBMSEmLCwsLMxIkTTU5OjlufDRs2mIsvvtj4+/ub1q1bm8cee6yhhuhS3Vjz8/PNlVdeaVq1amV8fX1N+/btzeTJk90u4zWmcYy1sjEC5vXXX3f1qavf2eXLl5u+ffsaPz8/06lTJ7f3aAhnGmt6erq59NJLTVRUlPH39zddunQxM2bMcLuvjzGeP9bbbrvNtG/f3vj5+ZlWrVqZoUOHugKTMU3n8zSm+rE2lc+zOqeHpqb02TqMMabh9muJiIiINE46p0lERESkBhSaRERERGpAoUlERESkBhSaRERERGpAoUlERESkBhSaRERERGpAoUlERESkBhSaRERERGpAoUlE5BTJyck4HI4KE4za5bLLLuPee++1uwwRQaFJRMQjeFpYE5GKFJpEREREakChSUQ80r///W969epFYGAgLVq0YNiwYeTl5QHw6quv0r17dwICAujWrRvPP/+823O/++47+vXrR0BAAAMHDuSjjz7C4XCQmppaq1q++eYbLrnkEgIDA2nbti133323qxaADh068Oijj3LbbbcRGhpKu3btePnll91eY9WqVfTt29dV08cff+yqad++fVx++eUAREZG4nA4uPXWW13PdTqdPPDAA0RFRREbG8vcuXNrNQ4ROUcNPkWwiMgZHDhwwPj4+Jinn37a7N2712zcuNE899xzJicnx/zzn/80cXFx5oMPPjB79uwxH3zwgYmKijILFy40xhiTk5NjWrVqZW666SazefNm89///td06tTJAGb9+vVnfO/ly5cbwBw/ftwYY8yuXbtMcHCw+dvf/mZ27NhhVq5cafr162duvfVW13Pat29voqKizHPPPWd27txp5s2bZ7y8vMz27duNMcZkZWWZqKgoc/PNN5stW7aYxYsXm65du7pqKi0tNR988IEBTFpamjl48KA5ceKEMcaaMT4sLMzMnTvX7Nixw7zxxhvG4XCYL774om7/0EXkjBSaRMTjrFu3zgBm3759FbZ17tzZ/Otf/3Jb9+c//9kkJiYaY4x56aWXTIsWLUxBQYFr+wsvvFDr0DRp0iRzxx13uPX53//+Z7y8vFzv0b59e3PzzTe7tjudThMdHW1eeOEF1/ufXtMrr7ziVtPp71vuF7/4hbn44ovd1l1wwQVm5syZZxyLiNQtH9t2cYmIVKFPnz4MHTqUXr16kZSUxJVXXsm1116Ln58fu3fvZtKkSUyePNnVv7S0lPDwcAC2bdtG7969CQgIcG1PTEysdS0bNmxg48aNvPXWW651xhicTid79+6le/fuAPTu3du13eFwEBsbS2ZmJgBpaWkVarrwwgtrXMOprw0QFxfnem0RaTgKTSLicby9vVm6dCmrVq3iiy++4Nlnn+XBBx/kv//9LwCvvPIKgwYNqvCc+pCbm8tvf/tb7r777grb2rVr53rs6+vrts3hcOB0Ouukhvp8bRGpOYUmEfFIDoeDiy66iIsuuog5c+bQvn17Vq5cSXx8PHv27GHcuHGVPq979+783//9H4WFha49O99++22t6+jfvz9bt26lS5cutX6NhIQE/vnPf1JUVIS/vz8Aa9ascevj5+cHQFlZWa3fR0Tql66eExGPs3r1ah599FHWrl1Leno6H374IYcPH6Z79+48/PDDzJs3j3/84x/s2LGDTZs28frrr/P0008DcNNNN+FwOJg8eTJbt25l8eLFPPnkk7WuZebMmaxatYpp06aRmprKzp07+eSTT5g2bVqNX+Omm27C6XRyxx13sG3bNj7//HNXTQ6HA4D27dvjcDhYtGgRhw8fJjc3t9Y1i0j9UGgSEY8TFhbG119/zVVXXUXXrl354x//yFNPPcWIESO4/fbbefXVV3n99dfp1asXv/jFL1i4cCEdO3YEICQkhP/+979s2rSJfv368eCDD/L444/XupbevXuzYsUKduzYwSWXXEK/fv2YM2cO8fHxZzWe//73v6SmptK3b18efPBB5syZA+DaG9a6dWsefvhhZs2aRUxMzFmFMhFpGA5jjLG7CBGR+rRv3z46duzI+vXr6du3r93lAPDWW28xceJEsrKyCAwMtLscEakBndMkItIA3nzzTTp16kTr1q3ZsGEDM2fO5Prrr1dgEmlEdHhORJqVKVOmEBISUukyZcqUenvfjIwMbr75Zrp37859993HddddV+Gu4SLi2XR4TkSalczMTLKzsyvdFhYWRnR0dANXJCKNhUKTiIiISA3o8JyIiIhIDSg0iYiIiNSAQpOIiIhIDSg0iYiIiNSAQpOIiIhIDSg0iYiIiNSAQpOIiIhIDSg0iYiIiNTA/weUAPrnmK+ShgAAAABJRU5ErkJggg==",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lighting attention encode phase:\n",
      "    seq_length  Triton FP32  Triton BF16       offical\n",
      "0        256.0  1445.158362  1179.984009   3493.651295\n",
      "1        512.0  1767.088032  1344.447970   4981.471872\n",
      "2        768.0  1938.016057  1374.240041   7858.732796\n",
      "3       1024.0  2163.494492  1444.809616   7929.071999\n",
      "4       1280.0  2472.287941  1581.276810   9033.264160\n",
      "5       1536.0  2681.855917  1596.182406  10395.296097\n",
      "6       1792.0  2945.689607  1678.188837  11716.121483\n",
      "7       2048.0  3196.016002  1761.184013  12891.942310\n",
      "8       2304.0  3455.392003  1858.752000  14413.065624\n",
      "9       2560.0  3754.371214  1966.400027  15541.366100\n",
      "10      2816.0  4018.294430  2012.560058  17300.535011\n",
      "11      3072.0  4251.359940  2099.392080  18014.192200\n",
      "12      3328.0  4507.699299  2183.059263  19362.316132\n",
      "13      3584.0  4858.348751  2338.323116  20773.104477\n",
      "14      3840.0  5058.803034  2392.054367  22046.899033\n",
      "15      4096.0  5370.521545  2491.750336  23453.728485\n"
     ]
    }
   ],
   "source": [
    "\n",
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['seq_length'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[256 * i for i in range(1, 16+1, 1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['Triton FP32', 'Triton BF16', 'offical'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton FP32\",\n",
    "            'Triton BF16',\n",
    "            \"offical\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-'), ('orange', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"lighting attention encode phase\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'h': 64, 'd': 128, 'b':4}\n",
    "        # args={'bs': 2, 'num_head': 32, 'rope_head_dim': 32, \n",
    "        #       'nope_head_dim': 64, 'kv_lora_rank': 256},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(b,seq_length,h,d, provider):\n",
    "    n = seq_length\n",
    "    dtype = torch.bfloat16\n",
    "    device = 'cuda'\n",
    "    qkv = torch.randn(b, n, h, d*3, device=device, dtype=dtype)\n",
    "    slope_rate = torch.rand(h, 1, 1, device=device, dtype=dtype)\n",
    "    attn_mask = torch.ones(b, n, device=device, dtype=torch.int32)\n",
    "    attn_mask[:, :n//10] = 0 \n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    quantiles = [0.1, 0.3, 0.7]\n",
    "    if provider == 'Triton FP32':\n",
    "        ms, *_ = triton.testing.do_bench(lambda: triton_lighting_attention(qkv, slope_rate, attention_mask=attn_mask, fp32=True), rep=100, quantiles=quantiles)\n",
    "    if provider == 'Triton BF16':\n",
    "        ms, *_ = triton.testing.do_bench(lambda: triton_lighting_attention(qkv, slope_rate, attention_mask=attn_mask), rep=100, quantiles=quantiles)\n",
    "    if provider == 'offical':\n",
    "        ms, *_ = triton.testing.do_bench(lambda: torch_lighting_attention(qkv, slope_rate, attention_mask=attn_mask), rep=100, quantiles=quantiles)\n",
    "\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "- decode阶段fp32和bf16差不多，使用fp32即可"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "image/png": "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sWTJEiM/P9+1VFRUuPa56667jMjISOPbb7811qxZYyQnJxvJyckWVt24TvcZZWdnG48++qixZs0aIzc31/j000+NLl26GBdeeKHFlTeO6dOnG0uXLjVyc3ONTZs2GdOnTzdsNpvx9ddfG4bROr8/Cjun8eKLLxqRkZGGp6enMWDAAGPFihVWl9Rk3HjjjUZ4eLjh6elpnHfeecaNN95oZGdnW12WpRYvXmwAxy1jx441DMN8/Pzhhx82QkNDDS8vL2Po0KFGRkaGtUU3slN9RhUVFcbll19utGvXzvDw8DA6depkjB8/vlX9A+NEnw1gvP322659Dh06ZNx9991GmzZtDF9fX+PXv/61kZ+fb13Rjex0n1FeXp5x4YUXGsHBwYaXl5cRExNj3H///UZJSYm1hTeS2267zejUqZPh6elptGvXzhg6dKgr6BhG6/z+aNZzERERadHUZ0dERERaNIUdERERadEUdkRERKRFU9gRERGRFk1hR0RERFo0hR0RERFp0RR2REREpEVT2BEREZEWTWFHRBrcxRdfzOTJkxv1Pbdv347NZnPNgdSQlixZgs1mO27yRBFpHhR2RKTJaWrhYvDgweTn5+NwOKwuRUTOgrvVBYiINHWenp6EhYVZXYaInCVd2RGRc6K2tpZJkybhcDho27YtDz/8MEem4vvb3/5Gv379CAgIICwsjJtvvpmioiLAvB11ySWXANCmTRtsNhu33norAE6nk7lz5xITE4OXlxeRkZH8+c9/rve+27Zt45JLLsHX15eePXuSkpJyRvXu2LGDq6++mjZt2uDn50fXrl353//+Bxx/peniiy/GZrMdt2zfvh2A4uJi7rjjDtq1a0dgYCCXXnopGzdu/CUfp4j8Ago7InJOLFiwAHd3d1atWsXzzz/Ps88+yxtvvAFATU0Njz32GBs3buTf//4327dvdwWajh078q9//QuAjIwM8vPzef755wGYMWMGTz75JA8//DBpaWm89957hIaG1nvfP/3pT0ybNo0NGzYQFxfHb3/7W2pra09b78SJE6mqquK7775j8+bNPPXUU/j7+59w348//pj8/HzXct111xEfH++q5frrr6eoqIgvvviCtWvX0qdPH4YOHcr+/fvP6rMUkV/I4lnXRaQFuuiii4zExETD6XS61j344INGYmLiCfdfvXq1ARgHDx40DMMwFi9ebADGgQMHXPuUlpYaXl5exuuvv37C18jNzTUA44033nCtS01NNQBj69atp625e/fuxuzZs0+47UT1HPHss88aQUFBRkZGhmEYhvH9998bgYGBRmVlZb39oqOjjf/7v/87bR0i0vB0ZUdEzolBgwZhs9lc7eTkZLKysqirq2Pt2rVcffXVREZGEhAQwEUXXQRAXl7eSV9v69atVFVVMXTo0FO+b48ePVw/h4eHA7hukZ3Kvffey+OPP86QIUOYNWsWmzZtOu0xX3zxBdOnT+fDDz8kLi4OgI0bN1JWVkZISAj+/v6uJTc3l5ycnNO+pog0PIUdEWlUlZWVDB8+nMDAQN59911Wr17NJ598AkB1dfVJj/Px8Tmj1/fw8HD9fCRsOZ3O0x53xx13sG3bNsaMGcPmzZvp168fL7744kn3T0tL46abbuLJJ5/k8ssvd60vKysjPDycDRs21FsyMjK4//77z+gcRKRhKeyIyDmxcuXKeu0VK1YQGxtLeno6+/bt48knn+SCCy4gISHhuCsvnp6eANTV1bnWxcbG4uPjw6JFi85ZzR07duSuu+7i448/5r777uP1118/4X579+7l6quvZtSoUUyZMqXetj59+lBQUIC7uzsxMTH1lrZt256z2kXk5BR2ROScyMvLY+rUqWRkZPD+++/z4osv8oc//IHIyEg8PT158cUX2bZtG//5z3947LHH6h3bqVMnbDYbn3/+OXv27KGsrAxvb28efPBBHnjgAd555x1ycnJYsWIFb775ZoPUO3nyZL766ityc3NZt24dixcvJjEx8YT7jho1Cl9fX2bPnk1BQYFrqaurY9iwYSQnJ3Pttdfy9ddfs337dpYvX86f/vQn1qxZ0yC1isjPo3F2ROScuOWWWzh06BADBgzAzc2NP/zhD9x5553YbDbmz5/PH//4R1544QX69OnDM888w69+9SvXseeddx6PPPII06dPZ9y4cdxyyy3Mnz+fhx9+GHd3d2bOnMnu3bsJDw/nrrvuapB66+rqmDhxIrt27SIwMJArrriC55577oT7fvfdd4AZyo6Wm5tL586d+d///sef/vQnxo0bx549ewgLC+PCCy887skxEWkcNsM4PPCFiIiISAuk21giIiLSoinsiEirMGLEiHqPgh+9PPHEE1aXJyLnkG5jiUir8OOPP3Lo0KETbgsODiY4OLiRKxKRxqKwIyIiIi2abmOJiIhIi6awIyIiIi2awo6IiIi0aAo7IiIi0qIp7IiIiEiLprAjIiIiLZrCjoiIiLRoCjsiIiLSov0/5fM3+Lx7S38AAAAASUVORK5CYII=",
      "text/plain": [
       "<Figure size 640x480 with 1 Axes>"
      ]
     },
     "metadata": {},
     "output_type": "display_data"
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "lighting attention decode phase:\n",
      "    batch_size  Triton FP32  Triton BF16     offical\n",
      "0          1.0    13.810877    13.489139  109.929165\n",
      "1          2.0    18.975944    18.716724  113.172515\n",
      "2          3.0    25.238146    24.574737  117.687179\n",
      "3          4.0    30.018988    29.877224  130.478693\n",
      "4          5.0    35.404936    35.768624  157.899168\n",
      "5          6.0    40.484864    40.926066  186.333029\n",
      "6          7.0    45.899834    45.482206  210.266890\n",
      "7          8.0    50.771921    51.169060  235.179392\n",
      "8          9.0    56.300555    55.819920  261.001221\n",
      "9         10.0    61.812394    61.518433  285.797118\n",
      "10        11.0    66.541705    66.659858  310.272857\n",
      "11        12.0    71.486241    71.035924  336.301586\n",
      "12        13.0    77.316052    75.987310  360.338310\n",
      "13        14.0    81.605392    81.336036  384.481666\n",
      "14        15.0    86.905932    86.345133  407.410012\n",
      "15        16.0    91.745157    92.217067  430.969455\n",
      "16        17.0    97.121829    97.139412  456.350412\n",
      "17        18.0   103.216736   102.533811  479.989333\n",
      "18        19.0   107.696000   108.043752  504.048986\n",
      "19        20.0   113.037959   113.256366  526.908750\n",
      "20        21.0   118.596583   118.662466  551.950569\n",
      "21        22.0   124.443756   123.498638  574.952269\n",
      "22        23.0   128.415396   128.245767  600.134344\n",
      "23        24.0   133.978608   133.055911  623.151862\n",
      "24        25.0   138.746938   138.301222  647.452796\n",
      "25        26.0   144.649755   143.360644  670.642052\n",
      "26        27.0   149.056560   148.441170  693.415052\n",
      "27        28.0   154.891239   154.120762  717.459368\n",
      "28        29.0   160.000776   159.109220  742.778716\n",
      "29        30.0   165.450667   165.358124  766.932341\n",
      "30        31.0   170.827000   170.461906  789.115518\n",
      "31        32.0   176.343003   175.540723  813.496815\n"
     ]
    }
   ],
   "source": [
    "\n",
    "torch.cuda.empty_cache()\n",
    "@triton.testing.perf_report(\n",
    "    triton.testing.Benchmark(\n",
    "        x_names=['batch_size'],  # argument names to use as an x-axis for the plot\n",
    "        x_vals=[i for i in range(1, 32+1, 1)],  # different possible values for `x_name`\n",
    "        line_arg='provider',  # argument name whose value corresponds to a different line in the plot\n",
    "        line_vals=['Triton FP32', 'Triton BF16', 'offical'],  # possible values for `line_arg``\n",
    "        line_names=[\n",
    "            \"Triton FP32\",\n",
    "            'Triton BF16',\n",
    "            \"offical\",\n",
    "        ],  # label name for the lines\n",
    "        styles=[('blue', '-'), ('green', '-'), ('orange', '-')],  # line styles\n",
    "        ylabel=\"ms\",  # label name for the y-axis\n",
    "        plot_name=\"lighting attention decode phase\",  # name for the plot. Used also as a file name for saving the plot.\n",
    "        args={'h': 64, 'd': 128, 'n':1}\n",
    "        # args={'bs': 2, 'num_head': 32, 'rope_head_dim': 32, \n",
    "        #       'nope_head_dim': 64, 'kv_lora_rank': 256},  # values for function arguments not in `x_names` and `y_name`\n",
    "    ))\n",
    "def benchmark(batch_size,n,h,d, provider):\n",
    "    b = batch_size\n",
    "    dtype = torch.bfloat16\n",
    "    device = 'cuda'\n",
    "    qkv = torch.randn(b, n, h, d*3, device=device, dtype=dtype)\n",
    "    kv =  torch.randn(b, h, d, d, dtype=torch.float32, device=device)\n",
    "    slope_rate = torch.rand(h, 1, 1, device=device, dtype=dtype)\n",
    "    stream = torch.cuda.Stream()\n",
    "    torch.cuda.set_stream(stream)\n",
    "    quantiles = [0.5, 0.3, 0.7]\n",
    "    if provider == 'Triton FP32':\n",
    "        ms = triton.testing.do_bench(lambda: triton_lighting_attention(qkv, slope_rate, kv, fp32=True), rep=100)\n",
    "    if provider == 'Triton BF16':\n",
    "        ms = triton.testing.do_bench(lambda: triton_lighting_attention(qkv, slope_rate, kv), rep=100)\n",
    "    if provider == 'offical':\n",
    "        ms = triton.testing.do_bench(lambda: torch_lighting_attention(qkv, slope_rate, kv), rep=100)\n",
    "\n",
    "    return ms * 1e3\n",
    "benchmark.run(show_plots=True, print_data=True)\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": null,
   "metadata": {},
   "outputs": [],
   "source": []
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.10.13"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
